// eee.Sheffield.PZ.Imaging
//
// Copyright ?Ping Zou, 2007
// sg71.cherub@gmail.com

using System;
using System.Collections.Generic;
using System.Text;
using eee.Sheffield.PZ.Math;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace eee.Sheffield.PZ.Imaging.ImageFilter
{
    public class AdaptiveSmooth : ImageFilter//, IGraylevelImageFilter
    {
        #region Fields
        private double _factor = 10.0;
        #endregion

        #region Properties
        public double Factor {get {return _factor;} set{_factor = value;}}
        #endregion

        #region Constructor
        public AdaptiveSmooth()
        {
            _flag = "[Image -> Smooth -> Adaptive Smooth]";
        }
        #endregion

        #region Apply filter
        protected override PZMath_matrix ApplyDoubleGraylevelImageFilter(PZMath_matrix srcMatrix, out string logText)
        {
            int width = srcMatrix.ColumnCount;
            int height = srcMatrix.RowCount;
            
            PZMath_matrix expandSrcMatrix = srcMatrix.BoundMirrorExpand(5, 5);
            int widthExpand = expandSrcMatrix.ColumnCount;
            int heightExpand = expandSrcMatrix.RowCount;
            
            PZMath_matrix expandDstMatrix = new PZMath_matrix(heightExpand, widthExpand);
            int widthExpandM2 = widthExpand - 2;
            int heightExpandM2 = heightExpand - 2;

            double f, gx, gy, weight, weightTotal, total;

            f = -8 * _factor * _factor;

            for (int y = 2; y < heightExpandM2; y++)
            {
                for (int x = 2; x < widthExpandM2; x++)
                {
                    weightTotal = 0;
                    total = 0;

                    // original formulas for weight calculation:
                    // w(x, y) = exp( -1 * (Gx^2 + Gy^2) / (2 * factor^2) )
                    // Gx(x, y) = (I(x + 1, y) - I(x - 1, y)) / 2
                    // Gy(x, y) = (I(x, y + 1) - I(x, y - 1)) / 2
                    //
                    // here is a little bit optimized version

                    // x - 1, y - 1
                    gx = expandSrcMatrix[y - 1, x] - expandSrcMatrix[y - 1, x - 2];
                    gy = expandSrcMatrix[y, x - 1] - expandSrcMatrix[y - 2, x - 1];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y - 1, x - 1];
                    weightTotal += weight;

                    // x, y - 1
                    gx = expandSrcMatrix[y - 1, x + 1] - expandSrcMatrix[y - 1, x - 1];
                    gy = expandSrcMatrix[y, x] - expandSrcMatrix[y - 2, x];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y - 1, x];
                    weightTotal += weight;

                    // x + 1, y - 1
                    gx = expandSrcMatrix[y - 1, x + 2] - expandSrcMatrix[y - 1, x];
                    gy = expandSrcMatrix[y, x + 1] - expandSrcMatrix[y - 2, x + 1];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y - 1, x + 1];
                    weightTotal += weight;

                    // x - 1, y
                    gx = expandSrcMatrix[y, x] - expandSrcMatrix[y, x - 2];
                    gy = expandSrcMatrix[y + 1, x - 1] - expandSrcMatrix[y - 1, x - 1];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y, x - 1];
                    weightTotal += weight;

                    // x, y
                    gx = expandSrcMatrix[y, x + 1] - expandSrcMatrix[y, x - 1];
                    gy = expandSrcMatrix[y + 1, x] - expandSrcMatrix[y - 1, x];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y, x];
                    weightTotal += weight;

                    // x + 1, y
                    gx = expandSrcMatrix[y, x + 2] - expandSrcMatrix[y, x];
                    gy = expandSrcMatrix[y + 1, x + 1] - expandSrcMatrix[y - 1, x + 1];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y, x + 1];
                    weightTotal += weight;

                    // x - 1, y + 1
                    gx = expandSrcMatrix[y + 1, x] - expandSrcMatrix[y + 1, x - 2];
                    gy = expandSrcMatrix[y + 2, x - 1] - expandSrcMatrix[y, x - 1];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y + 1, x - 1];
                    weightTotal += weight;

                    // x, y + 1
                    gx = expandSrcMatrix[y + 1, x + 1] - expandSrcMatrix[y + 1, x - 1];
                    gy = expandSrcMatrix[y + 2, x] - expandSrcMatrix[y, x];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y + 1, x];
                    weightTotal += weight;

                    // x + 1, y + 1
                    gx = expandSrcMatrix[y + 1, x + 2] - expandSrcMatrix[y + 1, x];
                    gy = expandSrcMatrix[y + 2, x + 1] - expandSrcMatrix[y, x + 1];
                    weight = System.Math.Exp((gx * gx + gy * gy) / f);
                    total += weight * expandSrcMatrix[y + 1, x + 1];
                    weightTotal += weight;

                    // save destination value
                    expandDstMatrix[y, x] = weightTotal == 0.0 ? expandSrcMatrix[y, x] : System.Math.Min(total / weightTotal, 255.0);
                }
            }

            PZMath_matrix dstMatrix = expandDstMatrix.BoundMirrorShrink(5, 5);

            logText = "succed";
            return dstMatrix;
        } // ApplyDoubleGraylevelImageFilter()

        protected override PZMath_matrix[] ApplyDoubleColourImageFilter(PZMath_matrix[] srcMatrixArray, out string logText)
        {
            // TODO: add this function
            throw new Exception("The method or operation is not implemented.");
        } // ApplyDoubleColourImageFilter()
        #endregion
    }
}
